package com.itany.shop.util;



import com.itany.shop.exception.DataAccessException;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 黄衡熙
 * @version 1.0
 */
public class JDBCTemplate<T> {
    /**
     * 执行一个DML操作。即:insert、delete、update
     * 对于DML操作,在整个操作流程中只有sql语句与需要参数不是固定的,其他步骤基本一致
     * 可以将不确定的部分提出出来作为方法的参数,将整个流程封装起来
     * @param sql    sql语句
     * @param params 参数,由于参数不是固定的,因此可以使用可变长参数来表示
     */
    public void update(String sql, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 获取连接
            conn = JDBCUtil.getConnection();
            // sql已经作为方法参数了,可以直接使用
            // 获取状态集
            ps = conn.prepareStatement(sql);
            // 设置参数
            // 由于不同的方法,其参数是不确定的,因此在当前模板方法中通过可变长参数表示了
            // 设置参数时需要遍历这个可变长参数,调用者传递了几个参数,则设置几次参数
            for (int i = 0; i < params.length; i++) {
                // 取出当前参数
                Object param = params[i];
                // 调用者传递的参数的类型可能不一样
                // 可以判断当前调用者需要的参数类型
                if (param instanceof String) {
                    ps.setString(i + 1, (String) param);
                } else if (param instanceof Integer) {
                    ps.setInt(i + 1, (Integer) param);
                } else if (param instanceof Double) {
                    ps.setDouble(i + 1, (Double) param);
                } else {
                    // 由于类型比较多,如果不想一个一个的判断,可以直接通过setObject表示所有类型
                    ps.setObject(i + 1, param);
                }
            }
            // 执行DML操作
            ps.executeUpdate();
        } catch (Exception e) {
            throw new DataAccessException("数据访问异常");
        } finally {
            // 此处释放资源时不要释放连接
            // 连接在事务操作中仍然需要用到
            JDBCUtil.close(null, ps, null);
        }
    }

    /**
     * 执行一个查询操作
     * 查询操作会返回一个结果集数据,其结果集可能是一条数据,也可能是多条数据
     * 可以通过一个对象集合表示返回值
     * @param sql
     * @param rm
     * @param params
     * @return
     */
    public List<T> query(String sql, RowMapper<T> rm, Object... params) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<T> list = new ArrayList<>();
        try {
            conn = JDBCUtil.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            while (rs.next()) {
                T t = rm.mapRow(rs);
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            throw new DataAccessException("数据访问异常");
        } finally {
            JDBCUtil.close(null, ps, rs);
        }
    }
}
